using System;
using System.Collections.Generic;
using System.Text;
using UvsChess;
using StudentAI.BitBoard;
using StudentAI.Algo;
using System.Threading;

namespace StudentAI
{
    public class StudentAI : IChessAI
    {
        #region IChessAI Members that are implemented by the Student

        /// <summary>
        /// The name of your AI
        /// </summary>
        public string Name
        {
#if DEBUG
            get { return "DeepClear"; }
#else
            get { return "StudentAI"; }
#endif
        }

        //Holds internal representation of our board
        private Board internalBoard = null;
        private bool initialized = false;
        
        /// <summary>
        /// Evaluates the chess board and decided which move to make. This is the main method of the AI.
        /// The framework will call this method when it's your turn.
        /// </summary>
        /// <param name="board">Current chess board</param>
        /// <param name="yourColor">Your color</param>
        /// <returns> Returns the best chess move the player has for the given chess board</returns>
       /*
        public ChessMove GetNextMove(ChessBoard board, ChessColor myColor)
        {
            Minimax minimax = new Minimax(new ScoreHeuristic(myColor));
            ChessMove move = minimax.alphaBeta(board, myColor);
            ChessBoard after = Movement.applyMove(board, move);
            ChessColor otherColor = myColor == ChessColor.White ? ChessColor.Black : ChessColor.White;
            ChessFlag flag = Movement.getFlag(after, otherColor);
            move.Flag = flag; 
            return move;
        } */
        public ChessMove GetNextMove(ChessBoard board, ChessColor myColor)
        {
            Log("MOVE START");
            long timelimit = DateTime.Now.Ticks + 10000 * 4970; //4.97 seconds per move
           // long timelimit = DateTime.Now.Ticks + 10000 * 270; //0.3 seconds per move
            if (!initialized)
            {
                initialize(board,myColor);
            }                     
            Log("Before: " +myColor+":"+internalBoard.Fen(true));
            Log("Score: " + internalBoard.Score);
            //Log("Before Z: " + internalBoard.hashBefore);

            ChessMove move;  
            //Check for stalemate
            //PieceValidMoves.SearchForMate(myColor, internalBoard,
            //            ref internalBoard.BlackMate, ref internalBoard.WhiteMate, ref internalBoard.StaleMate); 
            
            if (internalBoard.StaleMate)
            {
                Log("Offer stalemate");
                move = new ChessMove(new ChessLocation(0, 0), new ChessLocation(0, 0));
                move.Flag = ChessFlag.Stalemate;
                return move;
            }  
            
            internalBoard = Minimax.IterativeDeepening(internalBoard, this, timelimit);
            if (internalBoard == null)
            {
                Log("Offer stalemate");
                move = new ChessMove(new ChessLocation(0, 0), new ChessLocation(0, 0));
                move.Flag = ChessFlag.Stalemate;
                return move;
            }
            //Log("After " + myColor + ":" + internalBoard.Fen(false));
            MoveContent m = internalBoard.LastMove;
            if (m == null)
            {
                Log("Offer stalemate");
                move = new ChessMove(new ChessLocation(0, 0), new ChessLocation(0, 0));
                move.Flag = ChessFlag.Stalemate;
                return move;
            }

            int from = m.MovingPiece.SrcPosition;
            int xFrom = from % 8;
            int yFrom = from / 8;
            int to = m.MovingPiece.DstPosition;
            int xTo = to % 8;
            int yTo = to / 8;

            move = new ChessMove(new ChessLocation(xFrom, yFrom), new ChessLocation(xTo, yTo));
            Log("Move: " + move);
            
            #region set flags
            move.Flag = ChessFlag.NoFlag;
            if (myColor == ChessColor.White)
            {
                if (internalBoard.BlackCheck)
                {
                    move.Flag = ChessFlag.Check;
                }
                if (internalBoard.BlackMate)
                {
                    move.Flag = ChessFlag.Checkmate;
                }
            }
            if (myColor == ChessColor.Black)
            {
                if (internalBoard.WhiteCheck)
                {
                    move.Flag = ChessFlag.Check;
                }
                if (internalBoard.WhiteMate)
                {
                    move.Flag = ChessFlag.Checkmate;
                }
            }
            if (internalBoard.StaleMate)
            {
                move.Flag = ChessFlag.Stalemate;
            }
            #endregion
            return move;
        }
        /// <summary>
        /// Validates a move. The framework uses this to validate the opponents move.
        /// </summary>
        /// <param name="boardBeforeMove">The board as it currently is _before_ the move.</param>
        /// <param name="moveToCheck">This is the move that needs to be checked to see if it's valid.</param>
        /// <param name="turnColor">This is the color of the player who's making the move.</param>
        /// <returns>Returns true if the move was valid</returns>
        /*
        public bool IsValidMove(ChessBoard boardBeforeMove, ChessMove moveToCheck, ChessColor colorOfPlayerMoving)
        {
            return Movement.isValidMove(boardBeforeMove, moveToCheck, colorOfPlayerMoving);
        }
         */
        public bool IsValidMove(ChessBoard boardBeforeMove, ChessMove moveToCheck, ChessColor turnColor)
        {
            if (!initialized)
            {
                initialize(boardBeforeMove,turnColor);
            }
            //PieceValidMoves.SearchForMate(turnColor, internalBoard, ref internalBoard.BlackMate, ref internalBoard.WhiteMate, ref internalBoard.StaleMate);
            //Log("Checking move " + moveToCheck);
            //Log("board:    "+ boardBeforeMove.ToPartialFenBoard());
            //Log("internal: " + internalBoard.Fen(false));
            //Stalemates need to be checked first, because there isn't a valid move with them
            //Check that they didn't give a false stalemate
            if (!internalBoard.StaleMate && moveToCheck.Flag == ChessFlag.Stalemate)
            {
                Log("Enemy signaled false stalemate..We win");
                //Thread.Sleep(300);
                return false;
            }

            //Check that they signaled stalemate
            if (internalBoard.StaleMate && moveToCheck.Flag != ChessFlag.Stalemate)
            {
                Log("Enemy didn't signal stalemate..We win");
                //Thread.Sleep(300);
                return false;
            }

            PieceValidMoves.GenerateValidMoves(internalBoard); //NOTE: This clears the flags for mate/stale/check

            byte src = (byte)(moveToCheck.From.X + moveToCheck.From.Y*8);
            byte dst = (byte)(moveToCheck.To.X + moveToCheck.To.Y*8); 
            Piece piece = internalBoard.Squares[src];
            if (piece == null) 
            {
                Log("Null piece move");
                //Thread.Sleep(300);
                return false; //They tried to move an empty piece
            }

            if (piece.PieceColor != turnColor)
            {
                Log("Wrong color move");
                //Thread.Sleep(300);
                return false;
            }

            //At this point they have a valid piece they are moving
            foreach (byte pos in piece.ValidMoves)
            {
                //Make a copy of the board
                Board copy = internalBoard.FastCopy();                

                //Make move so we can examine it
                copy.MovePiece(src, pos, ChessPieceType.Queen);

                //We Generate Valid Moves for Board
                PieceValidMoves.GenerateValidMoves(copy);

                //Invalid Move
                if (copy.WhiteCheck && turnColor==ChessColor.White)
                {
                    continue;
                }

                //Invalid Move
                if (copy.BlackCheck && turnColor==ChessColor.Black)
                {
                    continue;
                }
                if (pos == dst)
                {
                    //Log("Found move " + pos +"("+moveToCheck.To+")");
                    //It is a valid move (pending incorrect flag)
                    //Do the move for real  
                    MoveContent move = internalBoard.MovePiece(src, dst, ChessPieceType.Queen);
                    PieceValidMoves.GenerateValidMoves(internalBoard);
                    //Check for false checks
                    if (moveToCheck.Flag == ChessFlag.Check && !internalBoard.WhiteCheck && !internalBoard.BlackCheck)
                    {
                        Log("Enemy signaled false check..We win " +
                            "w:" + internalBoard.WhiteCheck + ", b: " + internalBoard.BlackCheck);
                        //Thread.Sleep(300);
                        return false;
                    }                                                       
                    
                    if ((internalBoard.WhiteCheck || internalBoard.BlackCheck) && !(moveToCheck.Flag == ChessFlag.Check || moveToCheck.Flag==ChessFlag.Checkmate))
                    {
                        Log("Enemy didn't signal check..We win");
                        //Thread.Sleep(100);
                        return false;
                    }
                   
                    //Check that they did signal mate correcly
                    PieceValidMoves.SearchForMate(turnColor==ChessColor.Black ? ChessColor.White : ChessColor.Black, internalBoard, 
                        ref internalBoard.BlackMate, ref internalBoard.WhiteMate, ref internalBoard.StaleMate);                    

                    //Check that they didn't give a false mate
                    if (moveToCheck.Flag == ChessFlag.Checkmate && !internalBoard.WhiteMate && !internalBoard.BlackMate)
                    {
                        Log("Enemy signaled false mate..We win");
                        //Thread.Sleep(300);
                        return false;
                    }

                    if(moveToCheck.Flag != ChessFlag.Checkmate && (internalBoard.WhiteMate || internalBoard.BlackMate)){
                        Log("Enemy didn't signal mate..We win");
                        return false;
                    }
                    Log("Enemy Valid move");
                    return true;                                       
                }                
            }
            Log("No valid move found...");
            //Thread.Sleep(100);
            byte from = (byte)(moveToCheck.From.Y * 8 + moveToCheck.From.X);
            byte to = (byte)( moveToCheck.To.Y * 8 + moveToCheck.To.X);
            internalBoard.MovePiece(from,to, ChessPieceType.Queen);
            return false;
            //return true //testing only;
        }

        private void initialize(ChessBoard board, ChessColor turnColor)
        {
            Log("Deep clear is analyzing the universe.  It now knows Chess-Fu.");
            Board.initZobrist(); //Initialize random numbers for zobrist hashing, must be done before board is constructed
            PieceMoves.InitiateChessPieceMotion(); //Initialize bitboards
            this.internalBoard = new Board(board, turnColor); //Set the internal board
            internalBoard.setLogger(Log);

            /*
            Transposition tt = new Transposition(0,0,0,TranspositionType.empty);
            for (int i = 0; i < NegaMax.ttable.Length; i++)
            {
                NegaMax.ttable[i] = tt;
            }*/
            this.initialized = true;
            PieceValidMoves.GenerateValidMoves(internalBoard); //Generate first round of moves
        }

        #endregion

        #region IChessAI Members that should be implemented as automatic properties and should NEVER be touched by students.
        /// <summary>
        /// This will return false when the framework starts running your AI. When the AI's time has run out,
        /// then this method will return true. Once this method returns true, your AI should return a 
        /// move immediately.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        public AIIsMyTurnOverCallback IsMyTurnOver { get; set; }

        /// <summary>
        /// Call this method to print out debug information. The framework subscribes to this event
        /// and will provide a log window for your debug messages.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AILoggerCallback Log { get; set; }

        /// <summary>
        /// Call this method to catch profiling information. The framework subscribes to this event
        /// and will print out the profiling stats in your log window.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="key"></param>
        public AIProfiler Profiler { get; set; }

        /// <summary>
        /// Call this method to tell the framework what decision print out debug information. The framework subscribes to this event
        /// and will provide a debug window for your decision tree.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AISetDecisionTreeCallback SetDecisionTree { get; set; }
        #endregion
    }
}
